package uebung03_w;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;

import org.ejml.simple.SimpleEVD;
import org.ejml.simple.SimpleMatrix;

import tools.Arrow;
import tools.ColorConversions;
import tools.Derivative;
import tools.Gauss;
import tools.Image;

public class CopyOfKLT02 {
	public static void main(String[] args) throws Exception {

//		final BufferedImage imgI1 = ImageIO.read(CopyOfKLT02.class
//				.getClassLoader().getResource("res/lena1.png"));
//		final BufferedImage imgI2 = ImageIO.read(CopyOfKLT02.class
//				.getClassLoader().getResource("res/lena2.png"));

		
		final BufferedImage imgI1 = ImageIO.read(CopyOfKLT02.class
				.getClassLoader().getResource("res/s0.png"));
		final BufferedImage imgI2 = ImageIO.read(CopyOfKLT02.class
				.getClassLoader().getResource("res/s1.png"));

		final int w = imgI1.getWidth(), h = imgI1.getHeight();

		int[] intlumI1 = ColorConversions.getFastLuminance(
				imgI1.getRGB(0, 0, w, h, new int[w * h], 0, w), new int[w * h]);
		int[] intlumI2 = ColorConversions.getFastLuminance(
				imgI2.getRGB(0, 0, w, h, new int[w * h], 0, w), new int[w * h]);

		double[] lumI1 = Image.getScalarLuminance(intlumI1, new double[w * h]);
		double[] lumI2 = Image.getScalarLuminance(intlumI2, new double[w * h]);

		double[] dxI2 = Derivative.dx(lumI2, w, h, new double[w * h]);
		double[] dyI2 = Derivative.dy(lumI2, w, h, new double[w * h]);

		double[] dtI = Derivative.dt(lumI1, lumI2, new double[w * h]);

		final int step = 5, neighbours = 50;
		final ArrayList<Point2D> uv = new ArrayList<Point2D>(), xy = new ArrayList<Point2D>(), eigenValues = new ArrayList<Point2D>();

		SimpleMatrix sm = new SimpleMatrix(2, 2);

		for (int y = neighbours; y < h - neighbours; y += step) {
			for (int x = neighbours; x < w - neighbours; x += step) {

				double u = 0, v = 0;
				double hx = 1, hy = 1;
				double e1 = Double.NEGATIVE_INFINITY, e2 = Double.NEGATIVE_INFINITY;
				boolean take = false;
				double E1 = 0, E2 = 0, E5 = 0, E3 = 0, E6 = 0, sumW = 0;

				int counter = 0;

//				while ((hx * hx + hy * hy) > 0.1) {
				while ((hx * hx + hy * hy) > 1 && counter < 20) {
					counter++;
					try {
						System.out.println("hello "+counter+"hx "+hx+" hy "+hy);
						for (int j = -neighbours; j < neighbours; j++) {
							for (int i = -neighbours; i < neighbours; i++) {
								int index = (int) (i + x + u + (y + v + j) * w);
//								double dx = dxI2[index], dy = dyI2[index], dt = dtI[index];

								 double dx = Image.bilerp(dxI2, x+u, y+u, w,
								 h),
								 dy = Image.bilerp(dyI2, x+u, y+v, w, h), dt =
								 Image.bilerp(dtI, x+u, y+u, w, h);

								double weight = Gauss.gaussian1d(i * i + j * j,
										neighbours / 3.);

								weight = 1;
								// if (Double.compare(dt-dx-dy, 0)!=0)
								// System.out.println("dx: "+dx+" dy: "+dy+" dt: "+dt);

								E1 += dx * dx * weight;
								E2 += dy * dx * weight;
								E5 += dy * dy * weight;

								E3 -= dt * dx * weight;
								E6 -= dt * dy * weight;

								sumW += weight;
							}
						}

						E1 /= sumW;
						E2 /= sumW;
						E5 /= sumW;

						E3 /= sumW;
						E6 /= sumW;

//						System.out.println(E1 + " " + E2 + " " + E3 + " " + E5
//								+ " " + E6);

						double ll = (E2 * E2 - E1 * E5);

						sm.set(0, 0, E1);
						sm.set(1, 0, E2);
						sm.set(0, 1, E2);
						sm.set(1, 1, E5);

						if (Double.compare(0, ll) != 0) {
							if (Double.compare(sm.determinant(), 0) != 0) {
								SimpleEVD<SimpleMatrix> evd = sm.eig();

								take = true;

								e1 = Math.max(e1,
										Math.abs(evd.getEigenvalue(0).real));
								e2 = Math.max(e2,
										Math.abs(evd.getEigenvalue(0).real));

								hx = (E5 * E3 - E2 * E6) / ll;
								hy = (E2 * E3 - E1 * E6) / ll;

								u += hx;
								v += hy;

							}
						}

					} catch (Exception e) {
						// e.printStackTrace();
					}


				}
				if (take) {
					eigenValues.add(new Point2D.Double(e1, e2));

					xy.add(new Point2D.Double(x, y));
					uv.add(new Point2D.Double(u, v));
					System.out.println(uv.get(uv.size() - 1));

				}

			}

		}

		double max = Double.NEGATIVE_INFINITY;
		for (int i = 0; i < eigenValues.size(); i++) {
			Point2D p = eigenValues.get(i);
			max = Math.max(max,
					Math.max(Math.abs(p.getX()), Math.abs(p.getY())));
		}
		final double limit = max * 0.1;

		System.out.println(uv.size());

		JFrame ff = new JFrame();
		ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final AffineTransform zoom = AffineTransform.getScaleInstance(2, 2);

		final JComponent canvas = new JComponent() {
			{
				setPreferredSize(new Dimension(w * 2, h * 2));
			}
			Arrow arrw = new Arrow();

			@Override
			public void paint(Graphics g) {
				Graphics2D g2 = (Graphics2D) g;
				g2.transform(zoom);
				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
						RenderingHints.VALUE_ANTIALIAS_ON);
				g2.setRenderingHint(RenderingHints.KEY_RENDERING,
						RenderingHints.VALUE_RENDER_QUALITY);
				g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
						RenderingHints.VALUE_STROKE_PURE);
				g2.drawImage(imgI1, 0, 0, this);
				g2.setStroke(new BasicStroke(0.1f));

				for (int i = 0; i < uv.size(); i++) {
					Point2D eig = eigenValues.get(i);
					if (Math.max(eig.getX(), eig.getY()) > limit) {
						Point2D p1 = xy.get(i), p2 = uv.get(i);
						Point2D dst = new Point2D.Double(p1.getX() + p2.getX(),
								p1.getY() + p2.getY());

						arrw.setArrow(p1, dst, 1, 5);

						g2.setColor(Color.magenta);
						g2.fill(arrw);
						g2.setColor(Color.green);
						g2.draw(arrw);

					}
				}
				super.paint(g);
			}
		};
		canvas.addMouseWheelListener(new MouseWheelListener() {

			@Override
			public void mouseWheelMoved(MouseWheelEvent e) {

				double scale = Math.pow(1.01, -e.getWheelRotation());

				try {
					Point2D p = new Point2D.Double(e.getX(), e.getY());
					zoom.inverseTransform(p, p);
					zoom.concatenate(AffineTransform.getTranslateInstance(
							p.getX(), p.getY()));
					zoom.concatenate(AffineTransform.getScaleInstance(scale,
							scale));
					zoom.concatenate(AffineTransform.getTranslateInstance(
							-p.getX(), -p.getY()));
				} catch (NoninvertibleTransformException nte) {
					nte.printStackTrace();
				}
				canvas.repaint();
			}
		});
		ff.add(canvas, BorderLayout.CENTER);

		ff.pack();
		ff.setVisible(true);

	}
}
